package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;

import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private RedissonClient redissonClient;

    //订单购买
    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //1 远程调用 ：根据用户id获取用户信息
        Result<UserInfoVo> userInfoVoResult =
                userInfoFeignClient.getUserInfoVo(userId);
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        System.out.println(userInfoVo.getId());
        Assert.notNull(userInfoVo,"用户信息为空");
        //2 定义变量 ，为了封装数据使用

        //原始价格
        BigDecimal originalAmount = new BigDecimal("0.00");
        //优惠价格
        BigDecimal derateAmount = new BigDecimal("0.00");
        //最终价格
        BigDecimal orderAmount = new BigDecimal("0.00");
        //订单明细集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //订单减免明细集合
        ArrayList<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //3 判断订单类型  如果购买专辑
        //1001 购买专辑  1002 购买vip  1003 购买声音
        String itemType = tradeVo.getItemType();
        if("1001".equals(itemType)){
            //如果购买的是专辑
            //判断用户是否购买过专辑
            Long itemId = tradeVo.getItemId();

            Result<Boolean> isPaidResult =  userInfoFeignClient.isPaidAlbum(itemId,userId);
            Boolean isPaid = isPaidResult.getData();
            if(isPaid){
                //表明用户购买过专辑
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            //如果用户没有购买过此专辑，继续往下执行
            //  根据专辑Id 获取到专辑数据
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(itemId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo,"专辑数据为空");
            //判断用户是否是vip ，由此判断用户是享受普通折扣还是vip折扣
            Integer isVip = userInfoVo.getIsVip();
            Date vipExpireTime = userInfoVo.getVipExpireTime();
            if(isVip.intValue()==0){
                //表明不是vip,不能享受vip折扣，只能享受普通折扣
                BigDecimal discount = albumInfo.getDiscount();
                originalAmount = albumInfo.getPrice();
                //判断是否打折
                if(discount.intValue()!=-1){
                    //原价100 打八折  100*(10-8)/10=20,是优惠的价格
                    derateAmount = originalAmount.multiply(new BigDecimal("10")
                            .subtract(discount)).divide(new BigDecimal("10"),2,RoundingMode.HALF_UP);

                }
                orderAmount = originalAmount.subtract(derateAmount);

            } else if (isVip.intValue() == 1 && vipExpireTime.before(new Date())) {
                //表明用户是vip但是过期了，此时也不能享受vip折扣
                //表明不是vip,不能享受vip折扣，只能享受普通折扣
                BigDecimal discount = albumInfo.getDiscount();
                originalAmount = albumInfo.getPrice();
                //判断是否打折
                if(discount.intValue()!=-1){
                    //原价100 打八折  100*(10-8)/10=20,是优惠的价格
                    derateAmount = originalAmount.multiply(new BigDecimal("10")
                            .subtract(discount)).divide(new BigDecimal("10"),2,RoundingMode.HALF_UP);

                }
                orderAmount = originalAmount.subtract(derateAmount);
            }else{
                //表明用户是vip并且没有过期
                originalAmount = albumInfo.getPrice();
                if(albumInfo.getVipDiscount().intValue()!=-1){
                    derateAmount = originalAmount.multiply(new BigDecimal("10")
                            .subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"),2,RoundingMode.HALF_UP);

                }
                //最终价格
                orderAmount = originalAmount.subtract(derateAmount);
            }
            //向订单明细集合和订单减免明细集合中添加数据
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(itemId);
            orderDetailVo.setItemPrice(albumInfo.getPrice());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());

            orderDetailVoList.add(orderDetailVo);

            //订单减免明细集合
            if(derateAmount.doubleValue()!=0){
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVoList.add(orderDerateVo);
            }
        }
        else if ("1003".equals(itemType)) {
            //4 购买vip
            //远程调用：根据vip的id获取vip信息

        Result<VipServiceConfig> vipServiceConfigResult =  vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig,"vip数据为空");

            //设置原始价格  优惠价格  最终价格
            originalAmount =vipServiceConfig.getPrice();
            //折后价格
             orderAmount = vipServiceConfig.getDiscountPrice();
            //优惠价格
            derateAmount = originalAmount.subtract(orderAmount);

            //设置订单明细集合
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("vip会员"+vipServiceConfig.getName());
            orderDetailVo.setItemPrice(vipServiceConfig.getDiscountPrice());
            orderDetailVoList.add(orderDetailVo);
            //设置订单减免明细集合
            if(originalAmount!=orderAmount){
                //说明有折扣
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVoList.add(orderDerateVo);
            }

        }
        else if("1002".equals(itemType)){
            //  购买声音
            //远程调用:查询当前购买声音的数据列表

        Result<List<TrackInfo>> trackInfoResult = trackInfoFeignClient
                .findPaidTrackInfoList(tradeVo.getItemId(),tradeVo.getTrackCount());
            List<TrackInfo> trackInfoList = trackInfoResult.getData();

            //计算价格，购买声音没有折扣
            TrackInfo trackInfo = trackInfoList.get(0);
            Long albumId = trackInfo.getAlbumId();
            //通过专辑id获取单价
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            BigDecimal price = albumInfo.getPrice();
            if(tradeVo.getTrackCount().intValue()>0){
                originalAmount =new BigDecimal(tradeVo.getTrackCount()) .multiply(price);
                orderAmount = originalAmount;
            }else{
                originalAmount = price;
                orderAmount = originalAmount;
            }
            //计算订单明细集合
            orderDetailVoList = trackInfoList.stream().map(trackInfo1 -> {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemUrl(trackInfo1.getCoverUrl());
                orderDetailVo.setItemId(trackInfo1.getId());
                orderDetailVo.setItemName(trackInfo1.getTrackTitle());
                return orderDetailVo;
            }).collect(Collectors.toList());

        }

        //5 TODO 购买声音
        //6 把这些数据最终封装到OrderInfoVo
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //原始价格
        orderInfoVo.setOriginalAmount(originalAmount);
        //最终价格
        orderInfoVo.setOrderAmount(orderAmount);
        //优惠价格
        orderInfoVo.setDerateAmount(derateAmount);
        //订单明细
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        //优惠明细
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //购买类型
        orderInfoVo.setItemType(itemType);

        //7 防止重复提交
        //订单结算页面向redis中添加数据
        //redis的key是用户id，value是订单流水号(唯一的)
        String userKey = "user:trade:"+userId;
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", " ");
        redisTemplate.opsForValue().set(userKey,tradeNo);
        orderInfoVo.setTradeNo(tradeNo);
        //时间戳
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //8 数据安全校验
        //生成签名
        Map<String,Object> parameterMap = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);

        String sign = SignHelper.getSign(parameterMap);
        orderInfoVo.setSign(sign);
        //9 最终返回OrderInfoVo对象数据

        return orderInfoVo;


    }


    //提交订单接口
    @Override
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //校验数据安全问题
        String s = JSON.toJSONString(orderInfoVo);
        Map map = JSON.parseObject(s, Map.class);
        map.put("payWay",SystemConstant.ORDER_PAY_WAY_WEIXIN);
        SignHelper.checkSign(map);
        //判断是否重复提交
        String tradeNo = orderInfoVo.getTradeNo();
        if(StringUtils.isEmpty(tradeNo)){
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        //向redis中添加数据，key是用户id  value是订单流水号
//        String userKey = "user:trade:"+userId;
//        String tradeNo1 = (String) redisTemplate.opsForValue().get(userKey);
//        if(tradeNo.equals(tradeNo1)){
//            //说明是第一次提交 这时候要删除redis中的数据
//            redisTemplate.delete(userKey);
//        }else{
//            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
//        }

        //使用lua脚本实现
        String userKey = "user:trade:"+userId;
        String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) " +
                "then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Boolean> redisScript = new DefaultRedisScript<>(script,Boolean.class);
        Boolean flag = (Boolean)redisTemplate.execute(redisScript, Arrays.asList(userKey), tradeNo);
        if(!flag){
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        //下单，使用订单流水号作为订单号
      //判断支付类型  是余额支付还是微信支付

        if (orderInfoVo.getPayWay().equals("1001")){
            //微信支付
            //向相关表中添加数据
        }else if (orderInfoVo.getPayWay().equals("1003")){
            //账户余额支付
            //远程调用：判断余额是否充足 如果充足 扣减余额
            //传入的参数封装成对象传入进去
            AccountLockVo accountLockVo = new AccountLockVo();
            accountLockVo.setUserId(userId);
            accountLockVo.setOrderNo(tradeNo);
            //购买商品的金额
            accountLockVo.setAmount(orderInfoVo.getOrderAmount());
            //购买商品的名称
            accountLockVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        Result result  =  userAccountFeignClient.CheckAndDeduct(accountLockVo);
        if(result.getCode()!=200){
            //表示余额不足
            throw new GuiguException(result.getCode(),result.getMessage());
        }

            //向相关表中添加数据
         OrderInfo orderInfo =  this.SaveOrder(orderInfoVo,userId,tradeNo);
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(tradeNo);
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //购买的声音id   专辑id   vip的id集合
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream()
                    .map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //远程调用User模块：添加购买记录
         Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            if(userResult.getCode()!=200){
                throw new GuiguException(211,"新增购买记录异常!");
            }
        }

        return tradeNo;
    }



    @Transactional
    //向订单相关表添加数据
    public OrderInfo SaveOrder(OrderInfoVo orderInfoVo, Long userId, String tradeNo) {

        //有三张表
        //1 添加订单基本信息到order_Info表
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo,orderInfo);
        orderInfo.setUserId(userId);
        //订单支付状态为未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        String itemName = orderInfoVo.getOrderDetailVoList().get(0).getItemName();
        orderInfo.setOrderTitle(itemName);
        orderInfo.setOrderNo(tradeNo);

        orderInfoMapper.insert(orderInfo);
        //2 添加订单明细order_detail
        OrderDetail orderDetail = new OrderDetail();
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if(!CollectionUtils.isEmpty(orderDetailVoList)){
            orderDetailVoList.stream().forEach(orderDetailVo -> {
                orderDetail.setOrderId(orderInfo.getId());
                BeanUtils.copyProperties(orderDetailVo,orderDetailVo);
                orderDetailMapper.insert(orderDetail);
            });
        }


        //3 优惠明细 order_derate
        //注意 购买声音的时候  没有优惠
        if(orderInfoVo.getItemType()!=SystemConstant.ORDER_ITEM_TYPE_TRACK){
            OrderDerate orderDerate = new OrderDerate();
            List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
            if (!CollectionUtils.isEmpty(orderDerateVoList)){
                orderDerateVoList.forEach(orderDerateVo -> {
                    orderDerate.setOrderId(orderInfo.getId());
                    BeanUtils.copyProperties(orderDerateVo,orderDerate);
                    orderDerateMapper.insert(orderDerate);
                });
            }


        }

        //4 判断是余额支付的话 因为前面已经支付了 所以只需要修改订单状态为已经支付
        if(orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)){
            //修改订单状态  --- 已支付
            //根据orderNo查询订单数据
            //2  设置订单状态 已经支付
            //3 调用mapper方法进行修改
            if(orderInfo.getOrderStatus().equals("0901")){
                orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
                orderInfoMapper.updateById(orderInfo);
            }

        }
        //5 判断如果是微信支付  发送延迟消息
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_WAY_WEIXIN)){
            //使用redisson方式实现异步操作  发送延迟队列 如果超时未支付 取消订单
            //使用rabbitMQ实现
            //1 ttl+死信队列
            //2 延迟插件
            //Redisson 实现延迟消息
            this.sendDelayMessage(orderInfo.getId());
        }
        return orderInfo;
    }

    //发送延迟消息
    private void sendDelayMessage(Long id) {
        //  创建普通队列
        RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(MqConst.EXCHANGE_CANCEL_ORDER);

        //  把普通队列变为延迟队列
        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        //  向延迟队列发送消息 ，设置延迟时间
//        delayedQueue.offer(id.toString(),30, TimeUnit.MINUTES);
        delayedQueue.offer(id.toString(),10, TimeUnit.SECONDS);

    }


    //取消订单
    @Override
    public void orderCancel(long id) {
        OrderInfo orderInfo = orderInfoMapper.selectById(id);
        if(orderInfo.getOrderStatus().equals("0901")){
            //如果订单状态是未支付 才取消订单
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
        }
    }

}
